﻿//---------------------------------------------------------------------------
// <copyright file="RibbonWindow.cs" company="Microsoft Corporation">
//      Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------

namespace Microsoft.Windows.Controls.Ribbon
{
    #region Using declarations

    using System;
    using System.Security;
    using System.Security.Permissions;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Interop;
    using System.Windows.Media;
    using Microsoft.Windows.Themes;

    #endregion

    /// <summary>
    ///   A Ribbon specific Window class which allos the Ribbon to draw onto
    ///   the non-client area to overdraw the application menu and contextual tab groups.
    /// </summary>
    /// <SecurityNote>
    /// Critical: In PartialTrust we want to not allow creation or inheritance of window
    /// PublicOk: This has inheritance demand and constructor is blocked in partial trust
    /// </SecurityNote>
    [UIPermissionAttribute(SecurityAction.InheritanceDemand, Window = UIPermissionWindow.AllWindows)]
    public class RibbonWindow : Window
    {
        #region Constructor
        /// <summary>
        /// Initializes static members of the RibbonWindow class.
        /// Static Constructor.
        /// Sets the default Template.
        /// </summary>
        static RibbonWindow()
        {
            // This loading allows for Xbap since the Generic.xaml does not contain any reference to RibbonWindow
            _dictionary = new ResourceDictionary();

            // Store the dictionary so that when Templates are needed they are easy to find.
            _dictionary.Source = new Uri("/RibbonControlsLibrary;component/Themes/RibbonWindow.xaml", UriKind.Relative);
            _style = (Style)_dictionary[typeof(RibbonWindow)];
            StyleProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle)));
            DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(typeof(RibbonWindow)));

            // The Coervce Value Callback makes sure that the WindowStyle stays consistent with the Theme.
            WindowStyleProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceWindowStyle)));

            // Set up the commands for the fake chrome buttons
            CommandManager.RegisterClassCommandBinding(typeof(RibbonWindow), new CommandBinding(ApplicationCommands.Close, CloseWindowExecuted));
        }

        /// <summary>
        /// Initializes a new instance of the RibbonWindow class.
        /// Default Class contructor
        /// </summary>
        /// <SecurityNote>
        ///   Critical: Don't allow the creation of RibbonWindow's when in partial trust.
        ///   PublicOK: Base class Window demands full trust.  If at some point Window no
        ///             longer demands full-trust, nothing in this class will create
        ///             additional vulnerabilities.  The main concerns with creating top-level
        ///             windows are spoofing and pop-ups.
        /// </SecurityNote>
        [SecurityCritical]
        public RibbonWindow()
        {
            // Now check if the Glass/Aero Composoting is enabled.
            this.IsUsingGlass = NativeMethods.IsCompositionEnabled;

            // This holds the width of the window Border.  
            // The window border is respected at all times except for the top edge.
            this.CalcChromeSizing();

            // Hook the size changed event since we need to do a lot of non-standard sizing.
            this.SizeChanged += this.OnSizeChanged;

            // Setup the initial hook for when chrome is initialized.
            SourceInitialized += (sender, e) => this.HookCustomChrome();
            this.SetResourceReference(ThemeProperty, new ComponentResourceKey(typeof(Ribbon), "Theme"));
            this.SetResourceReference(SystemPramatersChangedProperty, SystemParameters.HighContrastKey);
        }

        #endregion

        #region Private Constants
        /// <summary>
        /// String set as the Theme property when in aero with glass
        /// </summary>
        private const string AeroThemeString = "aero";

        /// <summary>
        /// tring set as the Theme property when in aero with no glass, or when in luna
        /// </summary>
        private const string LunaThemeString = "luna";

        /// <summary>
        /// tring set as the Theme property when in classic
        /// </summary>
        private const string ClassicThemeString = "classic";

        /// <summary>
        /// String used to get the Classic Window Template
        /// </summary>
        private const string ClassicTemplateKeyString = "RibbonWindowClassicGlassOffTemplate";

        /// <summary>
        /// String used to get the Aero Glass On template
        /// </summary>
        private const string AeroGlassTemplateKeyString = "RibbonWindowAeroGlassOnTemplate";
        
        /// <summary>
        /// String used to get the Aero Glass Off Template
        /// </summary>
        private const string AeroNoGlassTemplateKeyString = "RibbonWindowAeroGlassOffTemplate";

        /// <summary>
        /// String used to get the Luna Template
        /// </summary>
        private const string LunaTemplateKeyString = "RibbonWindowLunaGlassOffTemplate";

        /// <summary>
        /// Luna's rounded corner X value used to CreateRoundedRect
        /// </summary>
        private const int RoundedCornerX = 17;

        /// <summary>
        /// Luna's rounded corner X value used to CreateRoundedRect
        /// </summary>
        private const int RoundedCornerY = 17;
        #endregion

        #region Static Methods
        /// <summary>
        /// This call back is used to monitor system parameters.
        /// </summary>
        /// <param name="sender">The parameter is not used.</param>
        /// <param name="o">The parameter is not used.</param>
        /// <returns>o parameter unchanged</returns>
        private static object SystemParametersCallback(DependencyObject sender, object o)
        {
            RibbonWindow r = sender as RibbonWindow;
            if ((r != null) && (r._templateApplied == true))
            {
                r.UpdateChromeState(true);
                r.ApplySizes();
            }

            return o;
        }

        /// <summary>
        /// This ensures the correct style dictionary is always used.
        /// </summary>
        /// <param name="o">This is unused</param>
        /// <param name="value">The value of the dictionary</param>
        /// <returns>The correct dictionary for the RibbonWindow.</returns>
        private static object OnCoerceStyle(DependencyObject o, object value)
        {
            if (value == null)
            {
                value = _style;
            }

            return value;
        }

        /// <summary>
        /// The Makes sure the WindowStyle is consistant with the theme.
        /// </summary>
        /// <param name="sender">RibbonWindow is the sender</param>
        /// <param name="value">Value being set.</param>
        /// <returns>The Value Set</returns>
        private static object OnCoerceWindowStyle(DependencyObject sender, object value)
        {
            RibbonWindow ribWin = sender as RibbonWindow;
            switch (ribWin.Theme)
            {
                case RibbonWindow.ClassicThemeString:
                case RibbonWindow.LunaThemeString:
                    return WindowStyle.None;
                case RibbonWindow.AeroThemeString:
                    if (ribWin.IsUsingGlass)
                    {
                        return WindowStyle.SingleBorderWindow;
                    }

                    return WindowStyle.None;
            }

            // This should never be hit.  But it makes the compiler happy.
            return value;
        }

        /// <summary>
        /// This is the call back for when the Theme Dependency Property is changed
        /// </summary>
        /// <param name="sender">The Ribbon Windon on which the theme is changing</param>
        /// <param name="e">Event Changed Args</param>
        private static void OnThemeChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            RibbonWindow ribWin = sender as RibbonWindow;
            if (ribWin != null)
            {
                if (ribWin._templateApplied)
                {
                    ribWin.UpdateChromeState(true);
                    ribWin.ApplySizes();
                }

                ribWin.CoerceValue(WindowStyleProperty);

                SetThemeTemplate(ribWin);
            }
        }

        /// <summary>
        /// This sets the template on a ribbon window based upon the current Theme
        /// </summary>
        /// <param name="ribWin">The RibbonWindow to set the theme on</param>
        private static void SetThemeTemplate(RibbonWindow ribWin)
        {
            // Apply the correct theme based upon the theme string just assigned.
            switch (ribWin.Theme)
            {
                case RibbonWindow.ClassicThemeString:
                    if (ribWin.Template != (ControlTemplate)_dictionary[RibbonWindow.ClassicTemplateKeyString])
                    {
                        ribWin.Template = (ControlTemplate)_dictionary[RibbonWindow.ClassicTemplateKeyString];
                    }

                    break;
                case RibbonWindow.AeroThemeString:
                    if (ribWin.IsUsingGlass)
                    {
                        if (ribWin.Template != (ControlTemplate)_dictionary[RibbonWindow.AeroGlassTemplateKeyString])
                        {
                            ribWin.Template = (ControlTemplate)_dictionary[RibbonWindow.AeroGlassTemplateKeyString];
                        }
                    }
                    else
                    {
                        if (ribWin.Template != (ControlTemplate)_dictionary[RibbonWindow.AeroNoGlassTemplateKeyString])
                        {
                            ribWin.Template = (ControlTemplate)_dictionary[RibbonWindow.AeroNoGlassTemplateKeyString];
                        }
                    }

                    break;
                case RibbonWindow.LunaThemeString:
                    if (ribWin.Template != (ControlTemplate)_dictionary[RibbonWindow.LunaTemplateKeyString])
                    {
                        ribWin.Template = (ControlTemplate)_dictionary[RibbonWindow.LunaTemplateKeyString];
                    }

                    break;
            }
        }
        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the visibility of the Icon in the RibbonWindow.
        /// </summary>
        public Visibility IconVisibility
        {
            get { return (Visibility)GetValue(IconVisibilityProperty); }
            set { SetValue(IconVisibilityProperty, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for IconVisibility.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty IconVisibilityProperty =
            DependencyProperty.Register("IconVisibility", typeof(Visibility), typeof(RibbonWindow), new UIPropertyMetadata(Visibility.Visible));

        /// <summary>
        /// Gets a value indicating whether Aero Glass is on or off
        /// This tells if this window currently has Windows Aero enabled and rendering the chrome on it.
        /// </summary>
        public bool IsUsingGlass
        {
            get { return (bool)GetValue(IsUsingGlassProperty); }
            private set { SetValue(IsUsingGlassPropertyKey, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for IsUsingGlass.  This enables animation, styling, binding, etc...
        /// </summary>
        private static readonly DependencyPropertyKey IsUsingGlassPropertyKey = DependencyProperty.RegisterReadOnly(
            "IsUsingGlass", 
            typeof(bool), 
            typeof(RibbonWindow), 
            new UIPropertyMetadata(true));
        
        /// <summary>
        /// Dependency Property for use with get IsUsingGlass
        /// </summary>
        public static readonly DependencyProperty IsUsingGlassProperty = IsUsingGlassPropertyKey.DependencyProperty;

        /// <summary>
        /// Gets the number of pixels that are reserved for Close Minimize and Restore buttons
        /// </summary>
        public double CaptionButtonsReservedWidth
        {
            get { return (double)GetValue(CaptionButtonsReservedWidthProperty); }
            private set { SetValue(CaptionButtonsReservedWidthPropertyKey, value); }
        }

        /// <summary>
        /// Using a DependencyPropertyKey as the backing store for IsUsingGlass.  This enables animation, styling, binding, etc...
        /// </summary>
        private static readonly DependencyPropertyKey CaptionButtonsReservedWidthPropertyKey = DependencyProperty.RegisterReadOnly(
            "CaptionButtonsReservedWidth", 
            typeof(double), 
            typeof(RibbonWindow), 
            new UIPropertyMetadata(0.0));
        
        /// <summary>
        /// Dependency Property for CaptionButtonsReservedWidth
        /// </summary>
        public static readonly DependencyProperty CaptionButtonsReservedWidthProperty = CaptionButtonsReservedWidthPropertyKey.DependencyProperty;
        
        /// <summary>
        ///     Gets or sets the string version of the current theme name.
        ///     Will apply a resource reference to the the current RibbonWindow
        ///     if this function has 
        /// </summary>
        private string Theme
        {
             get { return (string)GetValue(ThemeProperty); }
             set { SetValue(ThemeProperty, value); }
        }

        /// <summary>
        ///     Private property used to determine the theme name.
        /// </summary>
        private static readonly DependencyProperty ThemeProperty = DependencyProperty.Register(
            "Theme", 
            typeof(string), 
            typeof(RibbonWindow), 
            new FrameworkPropertyMetadata(String.Empty, new PropertyChangedCallback(OnThemeChangedCallback), null));

        /// <summary>
        /// This is used to make sure a template has been applied before
        /// sizing and layout is attempted.
        /// </summary>
        private bool _templateApplied = false;

        #endregion

        #region Private Data

        /// <summary>
        /// The style Dictionary.
        /// </summary>
        private static Style _style;

        /// <summary>
        /// Resource dictionary containing all the Templates for RibbonWindow.
        /// </summary>
        private static ResourceDictionary _dictionary;

        /// <summary>
        /// These are the private properties that hold the system metrics for the current window layout.
        /// </summary>
        private Thickness _borderThickness;

        /// <summary>
        /// The Height of the caption bar itself
        /// </summary>
        private double _captionHeight;

        /// <summary>
        /// Boder Thickness in Device Pixels
        /// </summary>
        private Thickness _borderThicknessDevicePixels;

        /// <summary>
        /// Caption Height in device pixels.
        /// </summary>
        private double _captionHeightDevicePixels;

        /// <summary>
        /// The Root Canvas.  This is used to make sure hit testing never hits this.
        /// </summary>
        private Canvas _rootCanvas;

        /// <summary>
        /// This is the content presenter.  We need to size and place it explicitly.
        /// </summary>
        private ContentPresenter _rootContentPresenter;

        /// <summary>
        /// This is the fake background. since we lose that but using a canvas as our root element
        /// </summary>
        private Border _backgroundBorder;

        /// <summary>
        /// This holds the close, minimize, and restor buttons.
        /// </summary>
        private Grid _buttonPanel;

        /// <summary>
        /// This is the ClassicBoderDecorator that is used to fake the old classic chrome.
        /// Its sized to zero when there glass is being drawn by aero.
        /// </summary>
        private ClassicBorderDecorator _innerChrome;

        /// <summary>
        /// This button is replacement for the normal windows mimimize button
        /// </summary>
        private Button _minimizeButton;

        /// <summary>
        /// This is the replacement for the normal restore button
        /// </summary>
        private Button _restoreButton;

        /// <summary>
        /// This is the replacement for the normal Maximize button
        /// </summary>
        private Button _maximizeButton;

        /// <summary>
        /// Window Icon
        /// </summary>
        private Image _icon;

        /// <summary>
        /// This property is used only to allow the CoerceValueCallback to make all the changes necessary to keep
        /// the window looking like the SystemParameters.
        /// </summary>
        private static readonly DependencyProperty SystemPramatersChangedProperty = DependencyProperty.Register(
            "SPChanged", 
            typeof(bool), 
            typeof(RibbonWindow),
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, null, new CoerceValueCallback(SystemParametersCallback)));

        /// <summary>
        /// Matrix Used to convert from device pixels to dpi independent pixels
        /// </summary>
        private Matrix _fromDevice;
 
        /// <summary>
        /// Matrix Used to convert from dpi independent pixels to device pixels
        /// </summary>
        private Matrix _toDevice;

        /// <summary>
        /// Should the next click on the icon be ignored.
        /// </summary>
        private bool _ignoreNextIconClick = false;
        #endregion

        #region Event and Command Handlers
        /// <summary>
        /// Command handler for minimize.
        /// </summary>
        /// <param name="sender">Object sending the command</param>
        /// <param name="args">Routed Args</param>
        private void MinimizeWindowClicked(object sender, RoutedEventArgs args)
        {
            this.WindowState = WindowState.Minimized;
        }

        /// <summary>
        /// Command handler for restore/maximize
        /// </summary>
        /// <param name="sender">Object sending the command</param>
        /// <param name="args">Routed Args</param>
        private void RestoreMaximizeWindowClicked(object sender, RoutedEventArgs args)
        {
            if (this.WindowState == WindowState.Maximized)
            {
                this.WindowState = WindowState.Normal;
            }
            else
            {
                this.WindowState = WindowState.Maximized;
            }
        }

        /// <summary>
        /// Command handler for Close
        /// </summary>
        /// <param name="sender">Object sending the command</param>
        /// <param name="args">Routed Args</param>
        private static void CloseWindowExecuted(object sender, ExecutedRoutedEventArgs args)
        {
            RibbonWindow w = sender as RibbonWindow;
            if (w != null)
            {
                w.Close();
            }
        }

        /// <summary>
        /// This is the hook that is called when the window is sized/resized.
        /// It calls _ApplySizes to get all the sizing set up.
        /// </summary>
        /// <param name="sender">Object Firing the event</param>
        /// <param name="e">Event Args</param>
        private void OnSizeChanged(object sender, EventArgs e)
        {
            this.ApplySizes();
        }
        #endregion

        #region Overrides
        /// <summary>
        /// This gets the parts of the template so that they can be sized.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // The template has been applied, and that means there is a composition target.  Get the dpi metrics.
            _fromDevice = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
            _toDevice = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
            
            _templateApplied = true;
            _rootCanvas = GetTemplateChild("PART_RootCanvas") as Canvas;
            _rootContentPresenter = GetTemplateChild("PART_RootContentPresenter") as ContentPresenter;
            _backgroundBorder = GetTemplateChild("PART_BackgroundBorder") as Border;
            _buttonPanel = GetTemplateChild("PART_ButtonPanel") as Grid;
            _innerChrome = GetTemplateChild("PART_InnerChrome") as ClassicBorderDecorator;

            // Now hook up events to the minimize and restore buttons.
            _minimizeButton = GetTemplateChild("PART_MinimizeWindowButton") as Button;
            _restoreButton = GetTemplateChild("PART_RestoreWindowButton") as Button;
            _maximizeButton = GetTemplateChild("PART_MaximizeWindowButton") as Button;
            _icon = GetTemplateChild("PART_Icon") as Image;
            if (_icon != null)
            {
                _icon.MouseLeftButtonDown += new MouseButtonEventHandler(IconMouseLeftButtonDown);
            }

            if (_maximizeButton != null)
            {
                _maximizeButton.Click += this.RestoreMaximizeWindowClicked;
            }

            if (_minimizeButton != null)
            {
                _minimizeButton.Click += this.MinimizeWindowClicked;
            }

            if (_restoreButton != null)
            {
                _restoreButton.Click += this.RestoreMaximizeWindowClicked;
            }
        }
        #endregion

        #region Private Methods
        /// <summary>
        ///   This function decides to either turn glass on or set the rounding rectangle on the window
        ///   and enable all of the self drawn chrome.
        /// </summary>
        /// <param name="force">Force window to either have its Glass Extended or to Have the rounding rectangle set</param>
        /// <SecurityNote>
        ///   Critical: This Extends dwm into the frame if IsUsingGlass is true.
        ///   TreatAsSafe: This merely affects visual appearance, and the application has no input
        ///                to the calculation of the glass area extent.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        private void UpdateChromeState(bool force)
        {
            bool aeroState = NativeMethods.IsCompositionEnabled;
            if (force || aeroState != this.IsUsingGlass)
            {
                this.IsUsingGlass = aeroState;

                if (!this.IsUsingGlass)
                {
                    if (this.Theme == RibbonWindow.ClassicThemeString)
                    {
                        // Classic has square corners.
                        this.SetRectRegion();
                    }
                    else
                    {
                        // rounded corners.
                        this.SetRoundedRegion();
                    }

                    this.CalcChromeSizing();
                    this.ApplySizes();
                }
                else
                {
                    // Must clear the rounding region first or .net will have the old cached values.
                    this.ClearRoundingRegion();

                    // Now get the correct sizes
                    this.CalcChromeSizing();

                    // Extend the glass down and in.
                    Thickness glassThickness = new Thickness(
                        _borderThicknessDevicePixels.Left,
                        _borderThicknessDevicePixels.Top + _captionHeightDevicePixels,
                        _borderThicknessDevicePixels.Right,
                        _borderThicknessDevicePixels.Bottom);

                    NativeMethods.ExtendNativeGlass(this, glassThickness);

                    // Set up the layout.
                    this.ApplySizes();
                }

                IntPtr hwnd = new WindowInteropHelper(this).Handle;
                NativeMethods.SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, NativeMethods.SWPFlags);

                // Make sure the Window Style is updated.
                this.CoerceValue(WindowStyleProperty);
            }
        }

        /// <summary>
        /// This function re-caches the SystemParameters that hold the Border and caption sizes.
        /// And Calculates the width of space the Caption Buttons(Close Minimize, Maximize, Restore) take up
        /// </summary>
        private void CalcChromeSizing()
        {
            _borderThickness = new Thickness(
                SystemParameters.ResizeFrameVerticalBorderWidth,
                SystemParameters.ResizeFrameHorizontalBorderHeight,
                SystemParameters.ResizeFrameVerticalBorderWidth,
                SystemParameters.ResizeFrameHorizontalBorderHeight);
            _captionHeight = SystemParameters.CaptionHeight;

            // Now calculate the width in actual pixels
            _borderThicknessDevicePixels = new Thickness(
                (_toDevice.M11 * _borderThickness.Left),
                (_toDevice.M22 * _borderThickness.Top),
                (_toDevice.M11 * _borderThickness.Right),
                (_toDevice.M22 * _borderThickness.Bottom));
            _captionHeightDevicePixels = _toDevice.M22 * _captionHeight;

            // Now determine the size of the reserved space for the close minimize and maximize buttons
            // These are all values determined by counting pixels in the buttons for close maximize and minimize
            switch (this.Theme)
            {
                case RibbonWindow.AeroThemeString:
                    this.CaptionButtonsReservedWidth = (Math.Ceiling(_captionHeight * 1.25) * 2) + Math.Ceiling(_captionHeight * 2.15) + 6;
                    break;

                case RibbonWindow.LunaThemeString:
                    this.CaptionButtonsReservedWidth = (_captionHeight  * 3);
                    break;

                default:

                    // 3 buttons 2 pixels of padding.
                    this.CaptionButtonsReservedWidth = ((_captionHeight - 4) * 3) + 6;
                    break;
            }

            this.SizeIcon();
        }

        /// <summary>
        /// This handles the click events on the window icon.
        /// </summary>
        /// <param name="sender">CLick event sender</param>
        /// <param name="e">The event args.</param>
        private void IconMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 1)
            {
                if (!this._ignoreNextIconClick)
                {
                    this.ShowSystemMenu();
                }
                else
                {
                    this._ignoreNextIconClick = false;
                }
            }
            else if (e.ClickCount == 2)
            {
                this._ignoreNextIconClick = false;
                this.Close();
            }   
        }

        /// <summary>
        /// This does the major work of sizing the elements depending upon window state and size.
        /// </summary>
        private void ApplySizes()
        {
            switch (this.Theme)
            {
                case RibbonWindow.AeroThemeString:
                    if (this.IsUsingGlass)
                    {
                        this.ApplySizesAeroGlassOn();
                    }
                    else
                    {
                        this.ApplySizesAeroGlassOff();
                    }

                    break;
                case RibbonWindow.LunaThemeString:
                    this.ApplySizesAeroGlassOff();
                    break;

                default:
                    this.ApplySizesClassic();
                    break;
            }
        }

        /// <summary>
        /// Sets the size of the Icon.
        /// </summary>
        private void SizeIcon()
        {
            if (this._icon != null)
            {
                if (this.Theme == RibbonWindow.LunaThemeString)
                {
                    this._icon.Width = this._captionHeight - this._borderThickness.Left - this._borderThickness.Right;
                    this._icon.Height = this._captionHeight - this._borderThickness.Top - this._borderThickness.Bottom;
                    this._icon.Margin = new Thickness(this._borderThickness.Left, 0, 0, 0);
                    this._icon.VerticalAlignment = VerticalAlignment.Center;
                }
                else
                {
                    this._icon.Width = this._captionHeight;
                    this._icon.Height = this._captionHeight - this._borderThickness.Top;
                    if (this.Theme == RibbonWindow.AeroThemeString && this.IsUsingGlass)
                    {
                        this._icon.Margin = new Thickness(this._borderThickness.Left, 0, 0, 0);
                    }
                    else
                    {
                        this._icon.Margin = new Thickness(0, (this._borderThickness.Top / 2), 0, 0);
                    }
                }
            }
        }

        /// <summary>
        /// Does the layout for Aero when glass is off, and when in luna
        /// </summary>
        private void ApplySizesAeroGlassOff()
        {
            double paddingx = _borderThicknessDevicePixels.Left / 2;
            double paddingy = _borderThicknessDevicePixels.Top / 2;
            if (this.WindowState == WindowState.Maximized)
            {
                // In a maximized window the window actual size and height are bigger than the number of pixels displayed.
                // The area given to the client area is the same as the visible area since there are no visible borders.
                _rootContentPresenter.Width = this.ActualWidth - (_borderThickness.Left + _borderThickness.Right);
                _rootContentPresenter.Height = this.ActualHeight - (_borderThickness.Bottom + _borderThickness.Top);

                _rootContentPresenter.Margin = new Thickness(_borderThickness.Left, _borderThickness.Top, _borderThickness.Right, _borderThickness.Bottom);

                _backgroundBorder.Height = Math.Max(0, this.ActualHeight - (_borderThickness.Top + _captionHeight) + paddingy);
                _backgroundBorder.Width = _rootContentPresenter.Width;

                Canvas.SetTop(_backgroundBorder, (_borderThickness.Top + _captionHeight));
                _buttonPanel.Margin = new Thickness(0, _borderThickness.Top, 0, 0);
                _buttonPanel.Width = _rootContentPresenter.Width;

                this.SetMaximizedRegion();
            }
            else if (this.WindowState == WindowState.Normal)
            {
                _rootContentPresenter.Width = this.ActualWidth - (_borderThickness.Left + _borderThickness.Right);
                _rootContentPresenter.Height = this.ActualHeight - (_borderThickness.Bottom + paddingy);

                _rootContentPresenter.Margin = new Thickness(_borderThickness.Left, 4, _borderThickness.Right, _borderThickness.Bottom);

                _backgroundBorder.Height = Math.Max(0, _rootContentPresenter.Height - (_captionHeight + _borderThickness.Top) + paddingy);
                _backgroundBorder.Width = _rootContentPresenter.Width;

                _buttonPanel.Margin = new Thickness(0, _borderThickness.Top, 0, 0);
                Canvas.SetLeft(_backgroundBorder, _borderThickness.Left);
                Canvas.SetLeft(_buttonPanel, _borderThickness.Left);
                Canvas.SetTop(_backgroundBorder, _borderThickness.Top + _captionHeight);
                _buttonPanel.Width = _rootContentPresenter.Width;
                this.SetRoundedRegion();
            }
        }

        /// <summary>
        /// This does the sizing and layout of the ribbon window when in classic theme
        /// </summary>
        private void ApplySizesClassic()
        {
            // This protects from a race condition where a new template has been not been applied yet.
            if (_innerChrome == null)
            {
                return;
            }

            if (this.WindowState == WindowState.Maximized)
            {
                _rootContentPresenter.Width = this.ActualWidth - (_borderThickness.Left + _borderThickness.Right);
                _rootContentPresenter.Height = this.ActualHeight - (_borderThickness.Top + _borderThickness.Bottom);

                _backgroundBorder.Width = _rootContentPresenter.Width;
                Canvas.SetTop(_rootContentPresenter, _borderThickness.Top);
                Canvas.SetTop(_backgroundBorder, ((_borderThickness.Top + _captionHeight) - _borderThickness.Top));
                _buttonPanel.Width = _rootContentPresenter.Width;

                // Set the border to the system border thickness since it will all be removed.
                _innerChrome.BorderThickness = _borderThickness;

                // Calculate the fake background height.  Its different than the the height if glass is on.
                _backgroundBorder.Height = Math.Max(0, this.ActualHeight - (_borderThickness.Top + _captionHeight + _borderThickness.Bottom + 1));

                // No place the Top of the Background Border.
                Canvas.SetTop(_backgroundBorder, ((_borderThickness.Top + _captionHeight + 1) - _borderThickness.Top));

                // This sets the bounding/clipping region for the window.
                // Cuts off the border regions.
                this.SetMaximizedRegion();
            }
            else if (this.WindowState == WindowState.Normal)
            {
                _buttonPanel.Margin = new Thickness(0, 0, 0, 0);
                _rootContentPresenter.Width = this.ActualWidth - (_innerChrome.BorderThickness.Left + _innerChrome.BorderThickness.Right);

                _rootContentPresenter.Height = this.ActualHeight - (_innerChrome.BorderThickness.Bottom + _innerChrome.BorderThickness.Top);
                _buttonPanel.Width = _rootContentPresenter.Width;

                // The extra pixel is built into the default windows.
                _backgroundBorder.Height = Math.Max(0, _rootContentPresenter.Height - _captionHeight - 1);
                _backgroundBorder.Width = _rootContentPresenter.Width;

                // now place the Root Content Presenter
                Canvas.SetLeft(_rootContentPresenter, 0);
                Canvas.SetTop(_rootContentPresenter, 0);

                Canvas.SetTop(_backgroundBorder, _captionHeight + 1);
                Canvas.SetLeft(_backgroundBorder, 0);
                this.SetRectRegion();
            }
        }

        /// <summary>
        /// Applies the sizing and layout when Aero Glass is on.
        /// </summary>
        private void ApplySizesAeroGlassOn()
        {
            double paddingx = 4 * _fromDevice.M11;
            double paddingy = 4 * _fromDevice.M22;

            // Check to see what the margins and sizing should be.
            if (this.WindowState == WindowState.Maximized)
            {
                // In a maximized window the window actual size and height are bigger than the number of pixels displayed.
                // The area given to the client area is the same as the visible area since there are no visible borders.
                _rootContentPresenter.Width = this.ActualWidth - (_borderThickness.Left + _borderThickness.Right);
                _rootContentPresenter.Height = this.ActualHeight - (_borderThickness.Bottom + paddingy);

                _rootContentPresenter.Margin = new Thickness(_borderThickness.Left, paddingy, _borderThickness.Right, _borderThickness.Bottom);

                _backgroundBorder.Height = Math.Max(0, this.ActualHeight - (_borderThickness.Top + _captionHeight) + paddingy);
                _backgroundBorder.Width = _rootContentPresenter.Width;

                Canvas.SetTop(_backgroundBorder, (_borderThickness.Top + _captionHeight));
                _buttonPanel.Margin = new Thickness(0, _borderThickness.Top, 0, 0);
                _buttonPanel.Width = _rootContentPresenter.Width;
            }
            else if (this.WindowState == WindowState.Normal)
            {
                _rootContentPresenter.Width = this.ActualWidth - (_borderThickness.Left + _borderThickness.Right);
                _rootContentPresenter.Height = this.ActualHeight - (_borderThickness.Bottom + paddingy);

                _rootContentPresenter.Margin = new Thickness(_borderThickness.Left, paddingy, _borderThickness.Right, _borderThickness.Bottom);

                _backgroundBorder.Height = Math.Max(0, _rootContentPresenter.Height - (_captionHeight + _borderThickness.Top) + paddingy);
                _backgroundBorder.Width = _rootContentPresenter.Width;

                _buttonPanel.Margin = new Thickness(0, _borderThickness.Top, 0, 0);
                Canvas.SetLeft(_backgroundBorder, _borderThickness.Left);
                Canvas.SetTop(_backgroundBorder, _borderThickness.Top + _captionHeight);
            }
        }

        /// <summary>
        /// Applies the clipped region when maximized.
        /// </summary>
        private void SetMaximizedRegion()
        {
            IntPtr hwnd = new WindowInteropHelper(this).Handle;

            /*
             * This sets the bounding box, just like normal.  Cutting off the edges of the window.
             * Each edge has the thickness of the border cut off
             */
            int xEnd = (int)((_rootCanvas.Width * _toDevice.M11) - _borderThicknessDevicePixels.Left) + 1;
            int yEnd = (int)((_rootCanvas.Height * _toDevice.M22) - _borderThicknessDevicePixels.Top) + 1;
            IntPtr hrgn = NativeMethods.CreateRectRgn(
                (int)(_borderThicknessDevicePixels.Left)-1, // top left X
                (int)(_borderThicknessDevicePixels.Top)-1, // Top left Y
                 xEnd,  // Bottom Right X
                 yEnd); // Bottom Right y
            NativeMethods.SetWindowRgn(hwnd, hrgn, NativeMethods.IsWindowVisible(hwnd));
        }

        /// <summary>
        /// This is the method that sets up rendering to the non client area.
        /// It adds the Custom WinProc procedure to the hwind hooks
        /// Tries to turn on glass
        /// Then sets up the window in the correct location
        /// </summary>
        private void HookCustomChrome()
        {
            // Setup the WndProc function.
            // This does the heavy lifting of dealing with NCCALCSIZE and other windows messages.
            HwndSource source = (HwndSource)PresentationSource.FromVisual(this);
            source.AddHook(new HwndSourceHook(this.WndProc));

            this.UpdateChromeState(true);

            // Get the hwnd handle
            IntPtr hwnd = new WindowInteropHelper(this).Handle;

            // Send the default flags we want our window to have.
            NativeMethods.SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, NativeMethods.SWPFlags);
        }

        /// <summary>
        /// This method is the hook on WndProc
        /// </summary>
        /// <param name="hwnd">This Hwnd that is generating the messages.</param>
        /// <param name="msg">This is the message being processed by WndProc</param>
        /// <param name="wParam">This is Message parameter.</param>
        /// <param name="lParam">Another Message parameter.</param>
        /// <param name="handled">Return value.  Has this message been fully handled.</param>
        /// <returns>Pointer to the returned values.</returns>
        /// <SecurityNote>
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            const int SIZE_MAXIMIZED = 2;
            IntPtr returnValue = IntPtr.Zero;
            var message = (NativeMethods.WM)msg;
            bool modified = false;

            if (Environment.OSVersion.Version.Major >= 6)
            {
                // This handles the glow and all the rest for the caption buttons
                handled = NativeMethods.DwmDefWindowProc(hwnd, msg, wParam, lParam, out returnValue);
            }

            switch (message)
            {
                case NativeMethods.WM.NCCALCSIZE:

                    // Handling this makes sure that we will handle the non client area ourselves.
                    handled = true;
                    break;

                case NativeMethods.WM.SETTEXT:
                case NativeMethods.WM.SETICON:

                    // These events cause the non client window area to be re-drawn
                    // To prevent windows from draving them, the visible flag is un-set. 
                    modified = NativeMethods.ModifyHwndStyle(hwnd, NativeMethods.WS.VISIBLE, 0);
                    returnValue = NativeMethods.DefWindowProc(hwnd, message, wParam, lParam);
                    if (modified)
                    {
                        NativeMethods.ModifyHwndStyle(hwnd, 0, NativeMethods.WS.VISIBLE);
                    }

                    handled = true;
                    break;

                case NativeMethods.WM.NCACTIVATE:
                    handled = true;

                    modified = NativeMethods.ModifyHwndStyle(hwnd, NativeMethods.WS.VISIBLE, 0);

                    /*
                     * This is an undocumented option
                     * Passing -1 keeps windows from drawing in the non client window.
                     */
                    returnValue = NativeMethods.DefWindowProc(hwnd, NativeMethods.WM.NCACTIVATE, wParam, new IntPtr(-1));
                    if (modified)
                    {
                        NativeMethods.ModifyHwndStyle(hwnd, 0, NativeMethods.WS.VISIBLE);
                    }

                    break;

                case NativeMethods.WM.NCRBUTTONUP:
                    if (NativeMethods.HT.CAPTION == (NativeMethods.HT)wParam.ToInt32())
                    {
                        this.ShowSystemMenu(new Point(NativeMethods.GET_X_LPARAM(lParam), NativeMethods.GET_Y_LPARAM(lParam)));
                    }

                    handled = false;
                    break;

                case NativeMethods.WM.NCHITTEST:

                    if (IntPtr.Zero == returnValue)
                    {
                        NativeMethods.HT ht = this.HitTest(hwnd, lParam);
                        returnValue = new IntPtr((int)ht);
                        handled = (ht != NativeMethods.HT.NOWHERE);
                    }

                    break;

                case NativeMethods.WM.DWMCOMPOSITIONCHANGED:

                    this.UpdateChromeState(false);
                    this.ApplySizes();
                    RibbonWindow.SetThemeTemplate(this);
                    break;

                case NativeMethods.WM.SIZE:
                    WindowState state = this.WindowState;
                    if (wParam.ToInt32() == SIZE_MAXIMIZED)
                    {
                        state = WindowState.Maximized;
                    }

                    this.UpdateMenu(state);
                    handled = false;
                    returnValue = IntPtr.Zero;
                    break;
            }

            return returnValue;
        }

        /// <summary>
        /// This sets which menu items in the System Menu are enabled and disabled based upon the current window state.
        /// </summary>
        /// <param name="state">Current window state, or the window state being transitioned into.</param>
        private void UpdateMenu(WindowState state)
        {
            const NativeMethods.MF ENABLED = NativeMethods.MF.ENABLED | NativeMethods.MF.BYCOMMAND;
            const NativeMethods.MF DISABLED = NativeMethods.MF.GRAYED | NativeMethods.MF.DISABLED | NativeMethods.MF.BYCOMMAND;

            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            bool modified = NativeMethods.ModifyHwndStyle(hwnd, NativeMethods.WS.VISIBLE, 0);
            
            IntPtr hmenu = NativeMethods.GetSystemMenu(hwnd, false);

            if (IntPtr.Zero != hmenu)
            {
                int styleFlags = NativeMethods.GetWindowLongPtr(hwnd, NativeMethods.GWL.STYLE).ToInt32();

                bool canMinimize = ((int)styleFlags & (int)NativeMethods.WS.MINIMIZEBOX) != 0;
                bool canMaximize = ((int)styleFlags & (int)NativeMethods.WS.MAXIMIZEBOX) != 0;
                bool canSize = ((int)styleFlags & (int)NativeMethods.WS.THICKFRAME) != 0;

                switch (state)
                {
                    case WindowState.Maximized:
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.RESTORE, ENABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MOVE, DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.SIZE, DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MINIMIZE, canMinimize ? ENABLED : DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MAXIMIZE, DISABLED);
                        break;
                    case WindowState.Minimized:
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.RESTORE, ENABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MOVE, DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.SIZE, DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MINIMIZE, DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MAXIMIZE, canMaximize ? ENABLED : DISABLED);
                        break;
                    default:
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.RESTORE, DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MOVE, ENABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.SIZE, canSize ? ENABLED : DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MINIMIZE, canMinimize ? ENABLED : DISABLED);
                        NativeMethods.EnableMenuItem(hmenu, NativeMethods.SC.MAXIMIZE, canMaximize ? ENABLED : DISABLED);
                        break;
                }

                if (modified)
                {
                    NativeMethods.ModifyHwndStyle(hwnd, 0, NativeMethods.WS.VISIBLE);
                }
            }
        }

        /// <summary>
        /// Called to remove the rounding window we set up to fake the chrome look.
        /// </summary>
        private void ClearRoundingRegion()
        {
            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            NativeMethods.SetWindowRgn(hwnd, IntPtr.Zero, NativeMethods.IsWindowVisible(hwnd));
        }

        /// <summary>
        /// Sets the Hwnd Bounding region to the normal rectangle, with width and height equal to expected values.
        /// </summary>
        private void SetRectRegion()
        {
            IntPtr hwnd = new WindowInteropHelper(this).Handle;

            int width;
            int height;

            NativeMethods.RECT windowRect;
            NativeMethods.GetWindowRect(hwnd, out windowRect);
            width = windowRect.Width;
            height = windowRect.Height;

            // Don't set the rounding region if we are just being minimized.
            if (this.WindowState != WindowState.Minimized)
            {
                IntPtr hrgn = NativeMethods.CreateRectRgn(0, 0, width, height);
                NativeMethods.SetWindowRgn(hwnd, hrgn, NativeMethods.IsWindowVisible(hwnd));
            }
        }

        /// <summary>
        /// Sets the Hwnd Bounding region to the rounded corner rectangle, with width and height equal to expected values.
        /// The Top left and Top right corners are rounded.
        /// </summary>
        private void SetRoundedRegion()
        {
            IntPtr hwnd = new WindowInteropHelper(this).Handle;

            int width;
            int height;

            NativeMethods.RECT windowRect;
            NativeMethods.GetWindowRect(hwnd, out windowRect);
            width = windowRect.Width;
            height = windowRect.Height;

            // Don't set the rounding region if we are just being minimized.
            if (this.WindowState != WindowState.Minimized)
            {
                IntPtr hrgnSquare = NativeMethods.CreateRectRgn(0, (RibbonWindow.RoundedCornerX / 2) - 1, width, height);
                IntPtr hrgnRound = NativeMethods.CreateRoundRectRgn(0, 0, width, height, RibbonWindow.RoundedCornerX, RibbonWindow.RoundedCornerY);
                IntPtr hrgn = NativeMethods.CreateRectRgn(0, 0, width, height);
                NativeMethods.CombineRgn(hrgn, hrgnSquare, hrgnRound, NativeMethods.COMBINE_RGN_OR);
                NativeMethods.SetWindowRgn(hwnd, hrgn, NativeMethods.IsWindowVisible(hwnd));
            }
        }

        /// <summary>
        /// This shows the system menu right below the window icon.
        /// </summary>
        private void ShowSystemMenu()
        {
            const uint WM_MOUSELEFT = 0x0201;

            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            NativeMethods.RECT nativeWindowRect;
            NativeMethods.GetWindowRect(hwnd, out nativeWindowRect);

            if (!ShowSystemMenu(new Point(nativeWindowRect.left + this._borderThickness.Left, nativeWindowRect.top + this._borderThickness.Top + this._captionHeight)))
            {
                // No Menu Item was clicked so  we need to make sure to remove the next clicks.
                NativeMethods.Message peekmsg;

                // Remove all the WM messages that are clicks over the Icon.
                bool found = NativeMethods.PeekMessage(out peekmsg, hwnd, WM_MOUSELEFT, WM_MOUSELEFT, 0x0000);
                if (found)
                {
                    Visual e = this.InputHitTest(ConvertToLocal(new Point(peekmsg.p.x, peekmsg.p.y))) as Visual;
                    if (e != null && e.IsDescendantOf(this._icon))
                    {
                        // This message is in the Icon so we need to make sure that its ignored
                        this._ignoreNextIconClick = true;
                    }
                }
            }
        }

        /// <summary>
        /// This show the right click menu on the caption bar.
        /// </summary>
        /// <param name="point">Point where the menu should display.</param>
        private bool ShowSystemMenu(Point point)
        {
            const uint TPM_RETURNCMD = 0x0100;
            const uint TPM_LEFTBUTTON = 0x0;
            bool retVal = false;
            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            IntPtr systemMenuPointer = NativeMethods.GetSystemMenu(hwnd, false);
            uint cmd = NativeMethods.TrackPopupMenuEx(systemMenuPointer, TPM_LEFTBUTTON | TPM_RETURNCMD, (int)point.X, (int)point.Y, hwnd, IntPtr.Zero);
            if (0 != cmd)
            {
                NativeMethods.PostMessage(hwnd, NativeMethods.WM.SYSCOMMAND, new IntPtr(cmd), IntPtr.Zero);
                retVal = true;
            }

            return retVal;
        }

        /// <summary>
        /// This method performs the test on what retrun value to send back to NCHITTEST system message in WndProc
        /// </summary>
        /// <param name="hwnd">This HWND used to get the x and y coords.</param>
        /// <param name="lParam">This contains the x and y values encoded in it.</param>
        /// <returns>Retruns the correct HT value to WndProc.</returns>
        private NativeMethods.HT HitTest(IntPtr hwnd, IntPtr lParam)
        {
            /* 
             * This is a big ugly method but its much better to do this in one function rather then going to wpf
             * Multiple times for window information.
             * -elclark
             */

            NativeMethods.HT result = NativeMethods.HT.NOWHERE;
            Point mousePoint = new Point(NativeMethods.GET_X_LPARAM(lParam), NativeMethods.GET_Y_LPARAM(lParam));

            // Get the window rectangle.
            Microsoft.Windows.Controls.Ribbon.NativeMethods.RECT nativeWindowRect;
            NativeMethods.GetWindowRect(hwnd, out nativeWindowRect);

            // Get frame rectangle, adjusted for the style without a caption.
            var frameRect = new Microsoft.Windows.Controls.Ribbon.NativeMethods.RECT();
            NativeMethods.AdjustWindowRectEx(ref frameRect, Microsoft.Windows.Controls.Ribbon.NativeMethods.WS.OVERLAPPEDWINDOW & ~Microsoft.Windows.Controls.Ribbon.NativeMethods.WS.CAPTION, false, Microsoft.Windows.Controls.Ribbon.NativeMethods.WS_EX.None);

            // Determine if hit test is for resizing, default middle (1,1).
            int row = 1;
            int column = 1;
            bool onResizeBorder = false;

            /* 
             * Determine if the point is at the top or bottom of the window.
             */

            if (mousePoint.Y >= nativeWindowRect.top && mousePoint.Y < nativeWindowRect.top + (int)(_borderThicknessDevicePixels.Top + _captionHeightDevicePixels))
            {
                // Mouse is over the top border or the caption
                onResizeBorder = (mousePoint.Y < (nativeWindowRect.top - frameRect.top));
                row = 0;
            }
            else if (mousePoint.Y < nativeWindowRect.bottom && mousePoint.Y >= nativeWindowRect.bottom - (int)_borderThicknessDevicePixels.Bottom)
            {
                row = 2;
            }

            // Determine if the point is at the left or right of the window.
            if (mousePoint.X >= nativeWindowRect.left && mousePoint.X < nativeWindowRect.left + (int)_borderThicknessDevicePixels.Left)
            {
                // Mouse is on the left border.
                column = 0;
            }
            else if (mousePoint.X < nativeWindowRect.right && mousePoint.X >= nativeWindowRect.right - (int)_borderThicknessDevicePixels.Right)
            {
                // Mouse is on the right border.
                column = 2;
            }

            // This creates a 2d array of the possible return values. The above indecies determine which values to return.
            var hitTests = new[,]
            {
                { NativeMethods.HT.TOPLEFT, onResizeBorder ? NativeMethods.HT.TOP : NativeMethods.HT.CAPTION, NativeMethods.HT.TOPRIGHT },
                { NativeMethods.HT.LEFT, NativeMethods.HT.NOWHERE, NativeMethods.HT.RIGHT },
                { NativeMethods.HT.BOTTOMLEFT, NativeMethods.HT.BOTTOM, NativeMethods.HT.BOTTOMRIGHT },
            };

            // This is the temporary return value.
            // Hit testing will determine if its the final return value.
            result = hitTests[row, column];

            // It looks like this should be in the caption area.  Check to make sure that the buttons are not being hit.
            if (result == NativeMethods.HT.CAPTION)
            {
                mousePoint = ConvertToLocal(mousePoint);

                // This is slower than I would like.
                // Find out if the click was hitting something.
                IInputElement hitElement = this.InputHitTest(mousePoint);

                // This probably needs to be cleaned up.
                if (hitElement != null)
                {
                    Visual v = hitElement as Visual;

                    // TODO: This needs to be changed to something a little bit better
                    if ((v != null) && v != _buttonPanel && v != _rootCanvas)
                    {
                        result = NativeMethods.HT.NOWHERE;
                        return result;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Converts a point on the screen into a point on the window.
        /// </summary>
        /// <param name="p">A Point on the screen</param>
        /// <returns>A point relative to the window.</returns>
        private Point ConvertToLocal(Point p)
        {
            Point retVal = new Point(p.X, p.Y);

            Microsoft.Windows.Controls.Ribbon.NativeMethods.RECT nativeWindowRect;
            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            NativeMethods.GetWindowRect(hwnd, out nativeWindowRect);

            Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
            retVal.Offset(-1 * nativeWindowRect.left, -1 * nativeWindowRect.top);
            retVal.X = retVal.X * m.M11;
            retVal.Y = retVal.Y * m.M22;

            return retVal;
        }
#endregion
    }
}
